package jobs;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.Serializable;
import java.util.zip.GZIPInputStream;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;

public class Zip implements Serializable {

	/**
	 * 
	 */
	private static final long serialVersionUID = -3280772347662934432L;

	/**
	 * Méthode permettant de compresser un tableau de byte avec gzip
	 * 
	 * @param in
	 *            tableau de bytes à compresser
	 * 
	 * @return byte[] tableau de bytes compressé
	 * 
	 * @throws IOException
	 *             DOCUMENT ME!
	 */
	public static byte[] zip(byte[] in) throws IOException {
		ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(in);
		int size = byteArrayInputStream.read(in);

		if (size == -1) { // fichier en entrée vide

			return in;
		} else {
			ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
			byteArrayOutputStream.write(intTobytes(size));

			byte[] byteTable;
			ZipOutputStream zipOutputStream = new ZipOutputStream(byteArrayOutputStream);
			zipOutputStream.write(in, 0, size);
			zipOutputStream.close();
			byteTable = byteArrayOutputStream.toByteArray();

			return byteTable;
		}
	}

	/**
	 * Méthode permettant de décompresser un tableau de byte avec gzip
	 * 
	 * @param in
	 *            tableau de bytes à décompresser
	 * 
	 * @return byte[] tableau de bytes décompressé
	 * 
	 * @throws IOException
	 *             DOCUMENT ME!
	 */
	public static byte[] unzip(byte[] in) throws IOException {
		if (in.length == 0) { // fichier en entrée vide

			return in;
		} else {
			ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(in);

			byte[] format = new byte[4];
			byteArrayInputStream.read(format, 0, 4);

			int size = bytesToint(format);

			ZipInputStream gZIPInputStream = new ZipInputStream(byteArrayInputStream);
			int posrel = 0;
			byte[] byteTable = new byte[size];
			int pos = 0;

			while (posrel != -1) {
				posrel = gZIPInputStream.read(byteTable, pos, size - pos);
				pos = pos + posrel;

				if (posrel == 0) {
					posrel = -1;
				}
			}

			gZIPInputStream.close();

			return byteTable;
		}
	}

	/**
	 * Méthode permettant de transformer un entier en tableau de bytes
	 * 
	 * @param i
	 *            entier à convertir
	 * 
	 * @return byte[] tableau de bytes correspondant à i
	 */
	private static byte[] intTobytes(int i) {
		byte[] byteTable = new byte[4];
		String binary = Integer.toBinaryString(i);

		while (binary.length() < 32) {
			binary = "0" + binary;
		}

		for (int j = 0; j < 4; j++) {
			byteTable[j] = binaryTobyte(binary.substring(8 * j, 8 * j + 8));
		}

		return byteTable;
	}

	/**
	 * Méthode permettant de convertir une chaine représentant du binaire en byte
	 * 
	 * @param binary
	 *            chaine à convertir
	 * 
	 * @return byte correspondant à la chaine
	 */
	public static byte binaryTobyte(String binary) {
		int b = 0;

		for (int i = 0; i < 8; i++) {
			String bin = binary.substring(i, i + 1);

			if (Integer.parseInt(bin) == 1) {
				if (i == 7) {
					b = b + 1;
				} else if (i == 6) {
					b = b + 2;
				} else if (i == 5) {
					b = b + 4;
				} else if (i == 4) {
					b = b + 8;
				} else if (i == 3) {
					b = b + 16;
				} else if (i == 2) {
					b = b + 32;
				} else if (i == 1) {
					b = b + 64;
				} else if (i == 0) {
					b = b + 128;
				}
			}
		}

		return (byte) b;
	}

	/**
	 * Méthode permettant de convertir un tableau de byte en entier
	 * 
	 * @param b
	 *            tableau de bytes à convertir
	 * 
	 * @return int entier correspondant
	 */
	private static int bytesToint(byte[] b) {
		int result = 0;

		for (int j = 0; j < 4; j++) {
			int i = b[j];

			if (i < 0) {
				i = 256 + i;
			}

			if (j == 3) {
				result = result + i;
			} else if (j == 2) {
				result = result + i * 256;
			} else if (j == 1) {
				result = result + i * 256 * 256;
			} else if (j == 0) {
				result = result + i * 256 * 256 * 256;
			}
		}

		return result;
	}
}
